Skip to content

ci: install Playwright's headless shell, not the whole of chromium - #471

Merged
lens0021 merged 2 commits into
mainfrom
claude/smoke-headless-shell
Aug 19, 2026
Merged

ci: install Playwright's headless shell, not the whole of chromium#471
lens0021 merged 2 commits into
mainfrom
claude/smoke-headless-shell

Conversation

@lens0021

Copy link
Copy Markdown
Collaborator

Stacked on #470, which is stacked on #469. Review the top commit; the bases merge first.

playwright install chromium fetches three things: Chrome for Testing (379 MiB unpacked), the headless shell (262 MiB) and ffmpeg (5 MiB). These specs run headless and record no video, so the shell is the one Playwright starts and the browser beside it is downloaded to be ignored. Asking for the shell by name skips it. ffmpeg arrives either way, so what this saves is the 379 MiB, about three fifths of the bytes in a 33s step.

Checked, not reasoned

Installed the shell alone into a browser directory of its own, pointed PLAYWRIGHT_BROWSERS_PATH at it so Playwright could find nothing else, and ran the suite against a real bake:

57 passed (14.6s)

--with-deps stays. What it installs is system libraries, which the shell needs as much as the full browser.

If the shell were ever not enough -- a spec asking for a headed browser, or for video -- Playwright fails naming the executable it cannot find, so this cannot quietly test the wrong thing.

Why not cache the browsers, as #461 suggested

zizmor refuses it. actions/cache in a job that also runs docker/build-push-action is its cache-poisoning audit, high severity:

error[cache-poisoning]: runtime artifacts potentially vulnerable to a cache poisoning attack
 53 |         uses: docker/build-push-action@... runtime artifacts usually published here
246 |       - uses: actions/cache@...           enables caching by default

Tried it first and read that, which is the same objection the comment two lines below this change already records about setup-node's caching. Fetching less needs no exception.

Refs #461.

@lens0021
lens0021 force-pushed the claude/smoke-headless-shell branch from c7643e4 to 358af35 Compare August 19, 2026 04:23
Base automatically changed from claude/smoke-bake-at-once to main August 19, 2026 04:29
`playwright install chromium` fetches Chrome for Testing (379 MiB unpacked) and the headless shell (262 MiB) and ffmpeg (5 MiB). These specs run headless and record no video, so the shell is the one Playwright starts and the browser beside it is downloaded to be ignored. Asking for it by name skips it. ffmpeg arrives either way.

That is 33s of the smoke job, and roughly three fifths of the bytes in it.

Checked rather than reasoned: installed the shell alone into a browser directory of its own, pointed PLAYWRIGHT_BROWSERS_PATH at it so nothing else could be found, and ran the suite against a real bake. 57 passed in 14.6s.

--with-deps stays. What it installs is system libraries, which the shell needs as much as the full browser.

The alternative was caching ~/.cache/ms-playwright, which the issue suggested. zizmor refuses it: actions/cache in a job that also runs docker/build-push-action is its cache-poisoning audit, high severity, and the comment two lines below this one already records the same objection to setup-node's caching. Fetching less needs no exception.

Refs #461.

---
_Generated by [Claude Code](https://claude.ai/code/session_935f02d1)_

Co-authored-by: Claude <noreply@anthropic.com>
lens0021 added a commit that referenced this pull request Aug 19, 2026
…470)

> Stacked on #469. Review the top commit; the base merges first.

The reproducibility check bakes the docs site a second time and diffs
it, and the two bakes were 209s of the smoke job's 321s. They are
independent by construction, so they can run together.

The reason it pays is that a bake is mostly one process. Only the skin
passes run beside each other, and even those are three on a four-core
runner, so a second bake fills cores the first leaves idle rather than
taking any from it.

## Measured

Two containers pinned to four cores, the runner's count, on an image
built from this PR's base:

| | wall clock |
|---|---|
| one bake alone | 118s |
| two, one after the other | 257s |
| **two at once, four cores between them** | **150s** |
| two at once, two cores each | 166s |

150s is what landed: no cpu flags, both bakes left to size themselves as
they do today. Capping each at two cores is slower, because that also
halves the concurrency of the part which does parallelise.

An earlier run of this benchmark gave the concurrent case a spurious win
by capping each container at four cores on a fourteen-core host. That is
eight cores between them, and not a runner at all. Discarded.

Peak combined resident memory was 1 GiB against the runner's 16, with no
OOM in either log. That was the risk worth measuring: two bakes at three
skin passes each is six MediaWiki boots at once.

Output is byte-identical in every configuration above, which is the
check itself agreeing.

## The step's own shell

Each bake writes to its own log, because two live logs interleave into
neither, and both are printed afterwards in collapsed groups.

Verified that a failure still fails the step: a bake pointed at a source
directory that does not exist ends it non-zero, and the success path
ends it zero. Ran the step's shell verbatim, not a paraphrase of it.

The source mount is read-only now that two containers share it, so
neither can alter what the other is reading. Only `wikven translate`
writes into src, and this is not that.

Refs #461.

## On the nine minutes in the issue

Worth recording, since it changes what is left to do. #461 measured 535s
wall clock on #458, dominated by `binary` at 467s. But `binary` is
path-filtered and does not run on a pull request that only touches PHP.
On #466, which did exactly that, all 21 checks started within two
seconds of each other and `smoke` at 317s was the longest:

```
317s  smoke
186s  coverage
145s  phan (master)
 52s  phpunit (REL1_46)
 43s  docker-image
```

So a PHP-only pull request already goes green in about five and a half
minutes, and `smoke` is the whole of its critical path. The nine minutes
belongs to image-touching pull requests, where `binary` is the long pole
and a separate question.

This PR and #471 take `smoke` after the two of them from 321s to roughly
190s.

Co-authored-by: Claude <noreply@anthropic.com>
@lens0021
lens0021 force-pushed the claude/smoke-headless-shell branch from 358af35 to dd9f30f Compare August 19, 2026 04:29
--with-deps is an apt call, and it has hung the smoke job twice in thirteen
hours. Both times azure.archive.ubuntu.com -- the mirror a runner on Azure is
meant to use -- answered nothing at all, apt fell back to
archive.ubuntu.com, took the four InRelease files and then went quiet on the
package indexes until the job was killed. Six hours the first time, and
thirty minutes the second, which is only because the timeout landed between
them.

Nothing here needed that call. The runner image carries Google Chrome,
Chromium, Edge and Firefox as installed packages, so the shared libraries the
headless shell wants are on the machine before we ask for anything. What
--with-deps bought was a dependency on a mirror we do not otherwise touch.

If the image ever stops shipping them, Playwright names the .so it cannot
find, the same way it names an executable it cannot find, so this cannot
quietly test the wrong thing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uv1RzRurUH6wrgV5E9PESQ
@lens0021
lens0021 merged commit ddeabd9 into main Aug 19, 2026
21 checks passed
@lens0021
lens0021 deleted the claude/smoke-headless-shell branch August 19, 2026 15:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants